import ELNode from "./type/ELNode";
import {ELStack} from "./ELStack";
import {GraphConfigData, NodeConfig} from "@logicflow/core";
import {ELType} from "./type/ELType";
import MyParse from './MyParse';

interface TextEntity {
    x: number,
    y: number,
    value: string,
}

export default class MyContext {
    public endPoints: Record<string, ELNode[]> = {};
    public sourceNum: Record<string, number> = {};

    private nodeMap: Record<string, ELNode> = {};

    private inLoopNode: Record<string, boolean> = {};

    public elStack: ELStack = new ELStack();

    public startId: string = "start"
    public endId: string = "end";

    public isEnd(id: string): boolean {
        return this.endId === id;
    }

    public isStart(id: string): boolean {
        return this.startId === id;
    }

    private initEdge(sourceId: string, endId: string) {
        if (!this.endPoints[sourceId]) {
            this.endPoints[sourceId] = [];
        }
        this.endPoints[sourceId].push(this.nodeMap[endId]);

        if (!this.sourceNum[endId]) {
            this.sourceNum[endId] = 0;
        }
        this.sourceNum[endId]++;
    }


    public init(logicFlow: GraphConfigData) {
        const idMap: Map<string, string> = new Map();
        //初始化所有节点并转化为ElNode
        logicFlow.nodes.forEach(node => {
            if (!node.id) return;
            const elNode: ELNode = this.initELNode(node);
            idMap.set(node.id, elNode.id);
            this.nodeMap[elNode.id] = elNode;
        })

        // 初始化start和end节点
        const start: ELNode = new ELNode();
        start.id = this.startId;
        this.nodeMap[this.startId] = start;

        const end: ELNode = new ELNode();
        end.id = this.endId;
        this.nodeMap[this.endId] = end;


        //遍历边
        logicFlow.edges.forEach(edge => {
            const sourceId = idMap.get(edge.sourceNodeId);
            const targetId = idMap.get(edge.targetNodeId);
            if (!sourceId || !targetId) return;
            //if节点特殊处理
            if (this.nodeMap[sourceId].type === ELType.IF) this.parseIFEdge((edge.text as TextEntity)?.value, sourceId, targetId);
            else this.initEdge(sourceId, targetId);
        })
        //检查节点，没有起点的连接到start，没有终点的连接到end
        logicFlow.nodes.forEach(node => {
            if (!node.id) return;
            const id: string | undefined = idMap.get(node.id);
            if (!id) return;
            if (this.inLoopNode[id]) return

            if (!this.endPoints[node.id]) {
                this.initEdge(id, this.endId);
            }
            if (!this.sourceNum[id]) {
                this.initEdge(this.startId, id);
            }
        })
        //没有节点时,只有start和end
        if (logicFlow.nodes.length === 0) {
            this.initEdge(this.startId, this.endId);
        }
    }

    /**
     * 保证IF节点一定有两个分支，尽管可能有undefined
     * 且true分支在前，false分支在后
     * @param text
     * @param sourceId
     * @param targetId
     * @private
     */
    private parseIFEdge(text: string, sourceId: string, targetId: string) {
        const target = this.nodeMap[targetId];
        let ends = this.endPoints[sourceId];
        //@ts-ignore
        if (!ends) ends = [undefined, undefined];
        if (this.isTrueText(text)) {
            if (ends[0]) ends[1] = ends[0];
            ends[0] = target;
        } else if (this.isFalseText(text)) {
            if (ends[1]) ends[0] = ends[1];
            ends[1] = target;
        } else {
            if (!ends[0]) ends[0] = target;
            else ends[1] = target;
        }
        this.endPoints[sourceId] = ends;

        if (!this.sourceNum[targetId]) {
            this.sourceNum[targetId] = 0;
        }
        this.sourceNum[targetId]++;
    }

    private isTrueText(text: string): boolean {
        const texts = ["是", "true", "True", "TRUE"];
        return !!texts.find(t => t === text);
    }

    private isFalseText(text: string): boolean {
        const texts = ["否", "false", "False", "FALSE"];
        return !!texts.find(t => t === text);
    }

    /**
     * 将logicFlow节点转化为ELNode
     *
     * @param lfNode Lf节点
     * @return {@link ELNode}
     */
    private initELNode(lfNode: NodeConfig) {
        const node: ELNode = new ELNode();
        node.id = lfNode.id as string;
        node.nodeId = lfNode.properties?.nodeId as string;
        node.tag = lfNode.properties?.tag as string;
        node.name = lfNode.properties?.name as string;
        node.type = this.typeFormat(lfNode.type);
        node.data = lfNode.properties?.data as string;

        //循环节点直接生成ELNode
        if (node.type === ELType.LOOP) {
            //@ts-ignore
            if (lfNode.startId) {
                //@ts-ignore
                const sNode = new ELNode(lfNode.startId);
                //@ts-ignore
                sNode.nodeId = lfNode.startNodeId;
                node.startNode = sNode;
            }

            //@ts-ignore
            if (lfNode.breakId) {
                //@ts-ignore
                const sNode = new ELNode(lfNode.breakId);
                //@ts-ignore
                sNode.nodeId = lfNode.breakNodeId;
                node.breakNode = sNode;
            }
            node.startNum = lfNode.properties?.startNum as number
            node.addChild(
                //@ts-ignore
                new MyParse(lfNode.flowData).parse()
            );

            //@ts-ignore
            //删除节点
            this.inLoopNode[lfNode.startId] = true
            //@ts-ignore
            this.inLoopNode[lfNode.breakId] = true
            //@ts-ignore
            lfNode.flowData.nodes.forEach(node => {
                const id = node.id as string;
                this.inLoopNode[id] = true;
            });
        }

        return node;
    }

    private typeFormat(lfType: string): ELType {
        if (lfType === "IF") return ELType.IF;
        if (lfType === "SWITCH") return ELType.SWITCH;
        if (lfType === "LOOP") return ELType.LOOP
        return ELType.ID;
    }

    public setSourceNum(node: ELNode, num: number) {
        this.sourceNum[node.id] = num;
    }

    public getSourceNum(node: ELNode) {
        const num = this.sourceNum[node.id]
        if (!num) return 0;
        return num;
    }

    public getEndNum(node: ELNode): number {
        const elNodes = this.endPoints[node.id]
        if (!elNodes) return 0;
        return elNodes.length;
    }

    public getEndList(node: ELNode): ELNode[] {
        let elNodes = this.endPoints[node.id];
        if (!elNodes) return [];
        return elNodes;
    }

    public push(node: ELNode) {
        this.elStack.push(node);
    }

    public pop(): ELNode {
        return this.elStack.pop();
    }

    public createStackEnv(node: ELNode) {
        this.elStack.create();
        const newNode = new ELNode();
        newNode.id = node.id;
        newNode.type = node.type;
        newNode.nodeId = node.nodeId;
        this.elStack.push(newNode);
    }

    public quitStackEnv() {
        return this.elStack.quit();
    }

    public setStackEndPoint(node: ELNode) {
        this.elStack.addEndPoint(node);
    }
}